package com.terrier.ui.core
{
    import com.terrier.ui.managers.WxToolTipManager;
    import com.terrier.ui.skins.IWxSkinBase;
    
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.geom.Point;

    /**
     * UI组件的基类
     * @author Jarvis.weng
     */
    public class WxComponent extends WxSprite implements IWxToolTipManagerClient, IWxDataRenderer
    {
        private static const DEFAULT_PRIORITY:int = 0;

        private static var s_renderPriority:int = DEFAULT_PRIORITY;

        private static var s_rendering:Boolean = false;

        private var _invalidLayout:Boolean;

        private var _invalidView:Boolean;

        private var _viewRenderPriority:int = 0;

        private var _layoutRenderPriority:int = 0;

        /**
         * @private
         */
        protected var _width:Number;

        /**
         * @private
         */
        protected var _height:Number;

        /**
         * @private
         */
        protected var _explicitWidth:Boolean;

        /**
         * @private
         */
        protected var _explicitHeight:Boolean;

        /**
         * @private
         */
        protected var _toolTip:Object;

        /**
         * @private
         */
        protected var _toolTipClass:Class;

        /**
         * @private
         */
        protected var _toolTipShowDelay:Number;

        /**
         * @private
         */
        protected var _toolTipHideDelay:Number;

        /**
         * @private
         */
        protected var _skin:IWxSkinBase;

        /**
         * @private
         */
        protected var _data:Object;
		
		/**
		 * @private
		 */
		protected var _concernChildLayout:Boolean;

        /**
         * 构造函数
         */
        public function WxComponent()
        {
            initialize();
        }

        /**
         * @private
         */
        protected function initialize():void
        {
            // nothing to do
        }

        //-----------------------------------
        //
        // 皮肤设置
        //
        //-----------------------------------
        /**
         * 通过皮肤ID来设置皮肤
         * @param skinID 皮肤ID
         */
        public function setSkinID(skinID:String):void
        {
            // nothing to do
        }

        /**
         * 设置皮肤
         * @param value 皮肤
         */
        public function setSkin(value:IWxSkinBase):void
        {
            _skin = value;
            if (_skin != null)
            {
                isNaN(_width) || !_explicitWidth ? _width = _skin.width : null;
                isNaN(_height) || !_explicitHeight ? _height = _skin.height : null;
            }
			
			updateLayout();
			updateView();
        }

        //------------------------------------
        //
        // ToolTip功能相关
        //
        //------------------------------------

        /**
         * @inheritDoc
         */
        public function get toolTip():Object
        {
            return _toolTip;
        }

        /**
         * @inheritDoc
         */
        public function set toolTip(value:Object):void
        {
            var oldTip:Object = _toolTip;
            _toolTip = value;
            WxToolTipManager.instance.registerToolTip(this, oldTip, _toolTip);
        }

        /**
         * @inheritDoc
         */
        public function get toolTipClass():Class
        {
            return _toolTipClass;
        }

        /**
         * @inheritDoc
         */
        public function set toolTipClass(value:Class):void
        {
            _toolTipClass = value;
        }

        /**
         * @inheritDoc
         */
        public function get toolTipHideDelay():Number
        {
            return _toolTipHideDelay;
        }

        /**
         * @inheritDoc
         */
        public function set toolTipHideDelay(value:Number):void
        {
            _toolTipHideDelay = value;
        }

        /**
         * @inheritDoc
         */
        public function get toolTipShowDelay():Number
        {
            return _toolTipShowDelay;
        }

        /**
         * @inheritDoc
         */
        public function set toolTipShowDelay(value:Number):void
        {
            _toolTipShowDelay = value;
        }

        /**
         * @inheritDoc
         */
        public function get toolTipPosition():Point
        {
            return null;
        }

        /**
         * @inheritDoc
         */
        public function set toolTipPosition(value:Point):void
        {
            // nothing to do
        }

        //-----------------------------------------------
        //
        // 数据相关
        //
        //-----------------------------------------------

        /**
         * 提供给组件的数据. 这个属性在组件作为数据渲染器时使用. 例如:List组件的渲染器
         * @return
         */
        public function get data():Object
        {
            return _data;
        }

        /**
         * @private
         */
        public function set data(value:Object):void
        {
            _data = value;
        }

        /**
         * 一个布尔值，表示组件是否关心自己的子对象的布局。如果为true，则子对象在自身的布局改变时会通知该父对象。
         * @return
         */
        public function get concernChildLayout():Boolean
        {
            return _concernChildLayout;
        }
		
		/**
		 * @private
		 */
		public function set concernChildLayout(value:Boolean):void
		{
			_concernChildLayout = value; 
		}

        /**
         * 组件的中心点位置. 默认是组件的正中心
         * @return
         */
        public function get centerPoint():Point
        {
            return new Point(width / 2, height / 2);
        }

        /**
         * @inheritDoc
         */
        override public function get height():Number
        {
            return isNaN(_height) ? super.height : _height;
        }

        /**
         * @inheritDoc
         */
        override public function set height(value:Number):void
        {
            _explicitHeight = true;

            _height = value;
            invalidateLayout();
        }

        /**
         * @inheritDoc
         */
        override public function get width():Number
        {
            return isNaN(_width) ? super.width : _width;
        }
        /**
         * @inheritDoc
         */
        override public function set width(value:Number):void
        {
            _explicitWidth = true;
            _width = value;
            invalidateLayout();
        }

        /**
         * 彻底清理组件和它的内部资源. 功能类似于析构函数.
         */
        public function dispose():void
        {
            for (var i:int = 0; i < numChildren; ++i)
            {
                var child:WxComponent = getChildAt(i) as WxComponent;
                if (child != null)
                {
                    child.dispose();
                }
            }
        }

        /**
         * 立即更新组件的视图.
         * 通常情况下,不应该直接调用该方法, 而应该调用对应的异步版本:<code>invalidateView()</code>方法, 确保每一帧只更新一次视图以便提高效率
         * @see #invalidateView
         */
        public function updateViewNow():void
        {
            updateView();
        }

        /**
         * 立即更新组件的布局.
         * 通常情况下,不应该直接调用该方法, 而应该调用对应的异步版本:<code>invalidteLayout()</code>方法, 确保每一帧只更新一次布局以便提高效率
         * @see #invalidateLayout
         */
        public function updateLayoutNow():void
        {
            updateLayout();
        }

        /**
         * 调用这个方法,让组件开始刷新自己的外观. 组件将在Flash Player的Render事件中刷新自己的外观, 每一帧仅刷新一次
         */
        public function invalidateView():void
        {
            if (s_rendering)
            {
                removeEventListener(Event.ENTER_FRAME, nextFrameInvalidateView);
                addEventListener(Event.ENTER_FRAME, nextFrameInvalidateView);
                return;
            }

            _viewRenderPriority = ++s_renderPriority;

            invalidateStageView();

            if (_invalidView)
            {
                return;
            }
            _invalidView = true;

            // here to do something else once;
        }

        /**
         * 调用这个方法,让组件开始刷新自己的布局. 组件将在Flash Player的Render事件中刷新自己的布局, 每一帧仅刷新一次
         */
        public function invalidateLayout():void
        {
            if (s_rendering)
            {
                removeEventListener(Event.ENTER_FRAME, nextFrameInvalidateLayout);
                addEventListener(Event.ENTER_FRAME, nextFrameInvalidateLayout);
                return;
            }

            _layoutRenderPriority = ++s_renderPriority;

            invalidateStageLayout();

            if (_invalidLayout)
            {
                return;
            }
            _invalidLayout = true;

            if (parent is WxComponent && (parent as WxComponent).concernChildLayout)
            {
                (parent as WxComponent).invalidateLayout();
            }
        }

        /**
         * @private
         */
        protected function updateView():void
        {
            // nothing to do
        }

        /**
         * @private
         */
        protected function updateLayout():void
        {
            if (_skin == null)
            {
                return;
            }

            _skin.width = _width;
            _skin.height = _height;

            if (_skin.customLayout)
            {
                _width = _skin.width;
                _height = _skin.height;
            }
        }

        private function monitorRender():void
        {
            stage.removeEventListener(Event.RENDER, startRender);
            stage.addEventListener(Event.RENDER, startRender, false, DEFAULT_PRIORITY);

            stage.removeEventListener(Event.RENDER, stopRender);
            stage.addEventListener(Event.RENDER, stopRender, false, -(s_renderPriority + 1));
        }

        static private function startRender(e:Event):void
        {
            EventDispatcher(e.currentTarget).removeEventListener(e.type, startRender);
            s_rendering = true;
        }

        static private function stopRender(e:Event):void
        {
            EventDispatcher(e.currentTarget).removeEventListener(e.type, startRender);
            s_rendering = false;
            s_renderPriority = DEFAULT_PRIORITY;
        }

        private function validateView(e:Event = null):void
        {
            _invalidView = false;

            EventDispatcher(e.currentTarget).removeEventListener(e.type, validateView);
            updateView();
        }

        private function validateLayout(e:Event = null):void
        {
            _invalidLayout = false;

            EventDispatcher(e.currentTarget).removeEventListener(e.type, validateLayout);
            updateLayout();
        }

        private function invalidateStageLayout(e:Event = null):void
        {
            if (stage == null)
            {
                addEventListener(Event.ADDED_TO_STAGE, invalidateStageLayout);
            }
            else
            {
                if (e != null)
                {
                    removeEventListener(Event.ADDED_TO_STAGE, invalidateStageLayout);
                }

                stage.removeEventListener(Event.RENDER, validateLayout);
                stage.addEventListener(Event.RENDER, validateLayout, false, -_layoutRenderPriority, true);

                monitorRender();
                stage.invalidate();
            }
        }

        private function invalidateStageView(e:Event = null):void
        {
            if (stage == null)
            {
                addEventListener(Event.ADDED_TO_STAGE, invalidateStageView);
            }
            else
            {
                if (e != null)
                {
                    removeEventListener(Event.ADDED_TO_STAGE, invalidateStageView);
                }

                stage.removeEventListener(Event.RENDER, validateView);
                stage.addEventListener(Event.RENDER, validateView, false, -_viewRenderPriority, true);

                monitorRender();
                stage.invalidate();
            }
        }

        private function nextFrameInvalidateLayout(e:Event):void
        {
            (e.currentTarget as EventDispatcher).removeEventListener(e.type, nextFrameInvalidateLayout);
            invalidateLayout();
        }

        private function nextFrameInvalidateView(e:Event):void
        {
            (e.currentTarget as EventDispatcher).removeEventListener(e.type, nextFrameInvalidateView);
            invalidateView();
        }
    }
}
